using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using static Unity.Mathematics.math;

// 使用 Burst 编译器优化该 Job，设定浮点精度为标准，模式为快速（牺牲一些精度换取性能）
// 揭开区域
[BurstCompile(FloatPrecision.Standard, FloatMode.Fast)]
struct RevealRegionJob : IJob
{
    // 表示当前的游戏网格，其中包含各个格子的状态（如是否为地雷、数字、是否已揭示等）
    public Grid grid;

    // 指定开始揭示区域的起始格子的 “行和列” 坐标（使用 int2 表示二维坐标）
    public int2 startRowColumn;

    // 用于深度优先搜索（DFS）的栈的当前大小，记录栈中有多少有效元素
    int stackSize;

    // Job 的执行入口，当 Unity 调度并运行该 Job 时，会执行此方法
    public void Execute ()
    {
        // 创建一个临时的 NativeArray，用作栈结构，用于深度优先遍历（DFS）可揭示的格子
        // 最大容量为网格中所有格子的数量（grid.CellCount），避免越界
        var stack = new NativeArray<int2>(grid.CellCount, Allocator.Temp);

        // 初始化栈的大小为 0，表示当前栈为空
        stackSize = 0;

        // 将起始坐标压入栈中
        PushIfNeeded(stack, startRowColumn);

        // 当栈中还有待处理的格子时（即 stackSize > 0），继续循环处理
        while (stackSize > 0)
        {
            // 从栈顶弹出一个坐标（通过减少 stackSize 来模拟栈的 Pop 操作）
            int2 rc = stack[--stackSize];

            // 尝试向当前格子的四个基本方向（上下左右）进行扩展，并判断是否需要压入栈中
            // 注意：这里的坐标加减是基于六边形/蜂窝状网格的局部坐标系

            PushIfNeeded(stack, rc - int2(1, 0)); // 上方格子
            PushIfNeeded(stack, rc + int2(1, 0)); // 下方格子
            PushIfNeeded(stack, rc - int2(0, 1)); // 左侧格子
            PushIfNeeded(stack, rc + int2(0, 1)); // 右侧格子

            // **********************
            // 六边形网格特殊处理部分
            // **********************
            // 根据当前列的奇偶性，决定斜方向上的行偏移
            // 如果列号是偶数，使用 +1 的行偏移；如果是奇数，使用 -1 的行偏移
            // 这是因为六边形网格中，奇偶行的连接方式不同（类似于蜂窝结构）
            rc.x += (rc.y & 1) == 0 ? 1 : -1;

            // 再尝试将当前格子连接的两个斜方向（类似六边形的左上/右上或左下/右下）压入栈中
            PushIfNeeded(stack, rc - int2(0, 1)); // 斜方向之一
            PushIfNeeded(stack, rc + int2(0, 1)); // 斜方向之二
        }
    }
    
    /// <summary>
    /// 判断某个坐标对应的格子是否应该被揭示（或压入栈中），如果可以则进行揭示操作
    /// </summary>
    /// <param name="stack">stack：用于 DFS 的栈</param>
    /// <param name="rc">rc：要检查的格子的行列坐标</param>
    void PushIfNeeded (NativeArray<int2> stack, int2 rc)
    {
        // 尝试根据行列坐标 rc.x（列）、rc.y（行） 获取该格子在网格中的实际索引 i
        // 如果该坐标在网格范围内，则返回 true 并赋值索引到 i
        if (grid.TryGetCellIndex(rc.x, rc.y, out int i))
        {
            // 获取当前格子的状态（比如是否是地雷、是否已标记、是否已揭示、数字是多少等）
            CellState state = grid[i];

            // 判断当前格子是否【未被标记且未被揭示】
            // 假设 CellState 有一个方法 IsNot(CellState.MarkedOrRevealed)，用于判断该格子既不是已标记也不是已揭示状态
            if (state.IsNot(CellState.MarkedOrRevealed))
            {
                // 如果当前格子的状态为 "零"（即周围没有地雷，CellState.Zero），
                // 则将该格子的坐标压入栈中，以便后续继续探索其相邻格子（实现区域自动展开）
                if (state == CellState.Zero)
                {
                    stack[stackSize++] = rc; // 压栈：将当前坐标加入待处理队列
                }

                // 无论该格子是不是 Zero，只要它未被揭示，就将其状态设置为已揭示（Revealed）
                // 并且增加网格中已揭示格子的总数（revealedCellCount）
                grid.RevealedCellCount += 1;
                grid[i] = state.With(CellState.Revealed); // 使用 With 方法更新状态（可能是位操作或状态组合）
            }
        }
    }
}